{ pkgs
, lib
, inputs
, options
, ...
}:

let
  # Import the filterOptions function
  filterOptions = import ./filterOptions.nix lib;

  # Filter options for documentation
  filterGitHooks =
    path: opt:
    if lib.lists.hasPrefix [ "git-hooks" "hooks" ] path then
      builtins.elemAt path 2 == "_freeformOptions"
      || builtins.elem (builtins.elemAt path 3) [
        "enable"
        "description"
        "packageOverrides"
        "settings"
      ]
    else
      true;

  getStorePath =
    p:
    lib.pipe p [
      (lib.strings.splitString "/")
      (lib.lists.take 4)
      (lib.strings.concatStringsSep "/")
    ];

  # Rewrite source declarations to GitHub URLs
  sources = [
    {
      name = getStorePath inputs.devenv.outPath;
      url = "https://github.com/cachix/devenv/blob/main";
    }
    {
      name = inputs.git-hooks.outPath;
      url = "https://github.com/cachix/git-hooks.nix/blob/master";
    }
    {
      name = inputs.treefmt-nix.outPath;
      url = "https://github.com/numtide/treefmt-nix/blob/main";
    }
  ];

  rewriteSource =
    decl:
    let
      prefix = getStorePath decl;
      source = lib.lists.findFirst (src: src.name == prefix) { } sources;
      path = lib.strings.removePrefix prefix decl;
      sourceUrl = source.url or (throw "Failed to rewrite source url for module: ${decl}");
      url = sourceUrl + path;
    in
    {
      name = url;
      inherit url;
    };

  # Speed up doc builds by skipping narinfo queries
  disableSubstitutes =
    drv:
    drv.overrideAttrs (_: {
      allowSubstitutes = false;
    });

  mkDocOptions =
    { opts
    , docOpts ? { }
    ,
    }:
    let
      optionsDoc = pkgs.nixosOptionsDoc (
        {
          options = filterOptions filterGitHooks (builtins.removeAttrs opts [ "_module" ]);
          transformOptions = opt: (opt // { declarations = map rewriteSource opt.declarations; });
        }
        // docOpts
      );
    in
    optionsDoc
    // {
      optionsAsciiDoc = disableSubstitutes optionsDoc.optionsAsciiDoc;
      optionsJSON = disableSubstitutes optionsDoc.optionsJSON;
      optionsCommonMark = disableSubstitutes optionsDoc.optionsCommonMark;
    };

  # Generate documentation for all options
  allOptions = mkDocOptions {
    opts = options;
  };

  # Default doc template
  defaultDoc = ''
    [comment]: # (Please add your documentation above this line)

    @AUTOGEN_OPTIONS@
  '';

  # Generate individual docs for languages, services, and process managers
  generateOptionDocs =
    opts:
    mkDocOptions {
      inherit opts;
      docOpts = {
        variablelistId = "options";
      };
    };

  # The docs to generate
  docs = [
    {
      options = options.languages;
      srcDir = "languages";
      outDir = "$out/docs/individual-docs/supported-languages";
    }
    {
      options = options.services;
      srcDir = "services";
      outDir = "$out/docs/individual-docs/supported-services";
    }
    {
      options = options.process.managers;
      srcDir = "process-managers";
      outDir = "$out/docs/individual-docs/supported-process-managers";
    }
  ];

  # Generate individual documentation files
  generateIndividualDocs = pkgs.stdenv.mkDerivation {
    name = "generate-individual-docs";
    src = ../src/individual-docs;
    allowSubstitutes = false;
    buildPhase = ''
      AUTOGEN_NOTICE="[comment]: # (Do not edit this file as it is autogenerated. Go to docs/individual-docs if you want to make edits.)"

      ${lib.concatStringsSep "\n" (
        lib.map (
          {
            options,
            srcDir,
            outDir,
          }:
          ''
            mkdir -p ${outDir}

            ${lib.concatStringsSep "\n" (
              lib.mapAttrsToList (name: options: ''
                srcFile=${srcDir}/${name}.md
                outFile=${outDir}/${name}.md
                optionsFile=${(generateOptionDocs options).optionsCommonMark}

                # Create output file with autogen notice
                echo "$AUTOGEN_NOTICE" > "$outFile"

                # Append source content or default template
                if [ -f "$srcFile" ]; then
                  tail -n +1 "$srcFile" >> "$outFile"
                else
                  echo "${defaultDoc}" >> "$outFile"
                fi

                # Process and substitute options in place
                substituteInPlace "$outFile" --subst-var-by AUTOGEN_OPTIONS "$(
                  echo "## Options"
                  echo
                  sed 's/^## /### /g' "$optionsFile"
                )"

              '') options
            )}
          ''
        ) docs
      )}
    '';
    installPhase = ''
      mkdir -p $out
      cp -r . $out/
    '';
  };

in
{
  devenv.warnOnNewVersion = false;

  # Expose the outputs for the flake and scripts to use
  outputs = {
    devenv-docs-options = allOptions.optionsCommonMark;
    devenv-docs-options-json = allOptions.optionsJSON;
    devenv-generate-individual-docs = generateIndividualDocs;
  };

  scripts."devenv-generate-doc-options" = {
    description = "Generate option docs";
    exec = ./scripts/generate-doc-options.sh;
  };

  scripts."devenv-generate-individual-docs" = {
    description = "Generate individual docs of all devenv modules";
    exec = ./scripts/generate-individual-docs.sh;
  };

  scripts."devenv-verify-individual-docs" = {
    description = "Generate missing template markdown files";
    exec = ./scripts/verify-individual-docs.sh;
  };

  scripts."devenv-generate-docs" = {
    description = "Generate lists of all languages and services";
    exec = import ./scripts/generate-docs.nix {
      inherit lib;
      inherit (options) languages services;
    };
  };
}
